import { copy, clearObj, isIncludeZh } from './baseFn.js'
import { attrArr, tagArr } from './attr.js'

// const compiler = require('vue-template-compiler');
const compiler = VueTemplateCompiler

const moduleSortPrev = window.moduleSort || ['common']

let historyLangObj = {}

function getTemplateNode(template) {
	return compiler.compile(template)
}
/**
 * 处理生成的key名，根据提供的字符串和类型，匹配特定的DOM标签或属性名。
 * @param {string} str 需要匹配的字符串。
 * @param {string} type 类型，默认为 'attr'，可选值为 'attr' 或其他，用于区分是属性名匹配还是标签名匹配。
 * @returns {string|undefined} 如果匹配成功返回对应的名称，否则返回 undefined。
 */
export function dealSpectialName(str, type = 'attr') {
	let name
	// 根据 type 参数决定使用哪个数组进行匹配
	for (let v of type !== 'attr' ? tagArr : attrArr) {
		// 使用正则表达式对输入字符串进行不区分大小写的匹配
		if (new RegExp(v.reg, 'i').test(str)) {
			name = v.name
			break;
		}
	}
	return name
}

/**
 * 处理对象文本，通过替换特定模板实现动态内容生成。
 * @param {string} text - 待处理的文本字符串，其中可能包含需要动态替换的对象模板。
 * @param {string} tStr - 默认模板标识符，用于标识需要被替换的模板部分，默认为'$t'。
 * @param {function} dealFn - 处理函数，用于对捕获到的模板标识符和名称进行具体处理。
 * @returns {string} - 处理后的文本字符串，其中的模板已被替换为相应的结果。
 */
export function objDeal(text, tStr = '$t', dealFn) {
	// 使用正则表达式查找所有花括号包围的片段，这些片段被认为是模板
	const reg = /\{.+\}/gs
	// 替换所有匹配到的模板
	return text.replaceAll(reg, str => {
		// 分割字符串以便逐行处理
		let strArr = str.split('\n')
		strArr = strArr.map(eachStr => {
			// 跳过非中文或包含注释的行
			if (!/[\u4e00-\u9fa5]+/.test(eachStr)) return eachStr
			if (/\/\/[^\n]*/.test(eachStr)) return eachStr
			// 处理每行中的模板，替换为调用dealFn后的结果
			return eachStr.replaceAll(/(\w+):\s{0,2}("|')((?!\\1).*?)\2/g, (eachStr2, ...rest) => {
				// 如果当前处理的是'value'，则不做处理直接返回，否则进行替换
				if (rest[0] === 'value') return eachStr2
				// 对模板名称进行特殊处理后，调用dealFn处理并返回结果
				let name = dealSpectialName(rest[0])
				return dealFn(eachStr2, tStr, name)
			})
		})
		// 将处理后的行重新组合为单一字符串
		return strArr.join('\n')
	})
}

/**
 * 从给定的对象路径中获取值。
 * @param {string} path 对象的路径，例如 'a.b.c'，用于定位深层次的属性值。
 * @param {object} obj 要从中获取值的对象。如果未提供，则默认使用 window.langData。
 * @returns 返回路径指定的值。如果路径不正确或指定的值不存在，则可能返回undefined或抛出错误。
 */
export function getObjValueFromPath(path, obj) {
	obj = obj || window.langData; // 如果未提供对象，则默认使用window.langData
	const arr = path.split('.'); // 将路径字符串分割成数组，以便逐级访问对象属性
	let data = obj;
	for (let i in arr) { // 遍历路径数组，逐步深入到目标属性
		data = data[arr[i]]
		if (typeof data !== 'object') { // 如果当前数据不是对象，检查是否到达最后一级或数据未定义
			if (i < arr.length - 1 || (i === arr.length - 1 && data !== undefined)) {
				// 对于异常情况的提示，todo: 此处逻辑可能有误，需要进一步确认
				nalert('路径可能有误，请确认')
				return data
			}
			return data
		}
	}
}

/**
 * 将对象的value作为key，key生成路径（用来中文去重已经获取已有的路径）
 * @param {Object} obj 要处理的对象
 * @param {String} basePath 当前处理路径的基路径
 * @param {Object} resObj 用于存储结果的对象，key为value，value为key组成的路径
 * @param {Array} sortArr 用于排序的数组，如果提供，则按照该数组排序对象的key
 * @param {Object} options 额外的选项
 * @param {Boolean} options.isAllPath 是否在已存在相同value时，添加新的路径而不是覆盖
 * @returns {Object} 处理后的结果对象
 */
export function objReverse(obj, basePath = '', resObj = {}, sortArr, { isAllPath = true }) {
	let keys = Object.keys(obj)
	// 根据sortArr对key进行排序
	sortArr && keys.sort((a, b) => {
		const sortIndexA = sortArr.indexOf(a)
		const sortIndexB = sortArr.indexOf(b)
		return sortIndexB - sortIndexA
	})
	for (let key of keys) {
		// 排除中文key，避免问题
		if (/[\u4e00-\u9fa5]/.test(key)) continue
		const path = basePath ? `${basePath}.${key}` : key
		const value = obj[key]
		if (typeof value === 'object') {
			// 递归处理嵌套对象
			objReverse(value, path, resObj, void 0, { isAllPath })
			continue
		}
		// 处理非对象值，生成或更新路径
		if (!resObj[value]) {
			resObj[value] = path
		} else if (isAllPath) {
			// 如果已存在相同值，根据isAllPath决定是否合并路径
			resObj[value] += ' / ' + path
		}
	}
	return resObj
}

// 倒转对象，用于匹配路径
export function getCommonCnReverse(isAllPath) {
	return objReverse(window.langData || {}, '', {}, moduleSortPrev, { isAllPath })
}

/**
 * 将对象的键和值转化为字符串
 * 用于变量对象以及生成提取出的文案
 * @param {Object} obj - 需要转换的对象
 * @param {boolean} forFile - 指示结果是用于文件还是其他用途，默认为true，用于文件
 * @returns {string} 转换后的字符串
 */
export function objToStr(obj, forFile = true) {
	// 获取对象的所有键
	const keys = Object.keys(obj)

	// 如果forFile为true，则对键进行排序
	forFile && keys.sort((a, b) => {
		// 数字部分相同时，按数字大小排序；字母部分按字典序排序
		if (a.replace(/\d/g, '') === b.replace(/\d/g, '')) {
			return +a.replace(/[A-Za-z]/g, '') - +b.replace(/[A-Za-z]/g, '')
		}
		return a > b ? 1 : -1
	})

	// 将每个键值对转换为字符串，并按逗号连接
	let textRes = keys.map(key => {
		// 如果键不等于值，则在值两端添加单引号
		let data = obj[key]
		let text = `${key}`
		if (key !== data) {
			text += `: ${forFile ? "'" : ''}${data}${forFile ? "'" : ''}`
		}
		return text
	}).join(`,${forFile ? '\n' : ' '}`)

	// 如果是用于文件，且有内容，则在末尾添加逗号
	if (forFile && textRes) textRes += ','

	return textRes
}

/**
 * 将对象中的data类型属性转换为有意义的变量名，并更新文本中的占位符
 * @param {Object} obj - 原始对象，包含以data开头的属性
 * @param {string} text - 包含占位符{dataX}的文本字符串
 * @returns {string} 更新后的文本，占位符被替换为新的变量名
 */
export function dealDataToMeaningful(obj, text) {
	// 如果原始对象中没有data1属性，则直接返回文本不做处理
	if (!obj.data1) return text
	let num = 0
	let oldObj = { ...obj } // 创建原始对象的深拷贝
	let deleteNum = clearObj(obj, 'data') // 清除原始对象中的data属性，返回删除的数量

	// 循环处理每个被删除的data属性
	for (let i = 0; i < deleteNum; i++) {
		// 获取被删除属性的值，并去除首尾空格
		const value = oldObj[`data${i + 1}`].trim()
		// 尝试获取值中最后一个英文单词作为新的键名
		let finalStr = value.split('.').at(-1)
		let key = ''

		// 如果最后一个单词不存在或不全为英文，则自增生成新的键名
		if (!finalStr || !/^[a-zA-Z]+$/.test(finalStr)) {
			num++
			key = `data${num}`
		} else {
			key = finalStr
		}

		// 使用新的键名更新对象，并在文本中替换对应的占位符
		obj[key] = value
		text = text.replace(`{data${i + 1}}`, `{${key}}`)
	}

	return text
}

/**
 * 处理文本，提取其中的数字或容量单位并将其替换为占位符
 * @param {string} text - 需要处理的文本
 * @returns {Array} 返回一个包含处理后的文本和提取的数字或容量单位对象的数组
 *                   其中数组第一个元素为处理后的文本，第二个元素为一个对象，
 *                   对象的键值对形式为：'num1'/'size1' => "'数字或容量单位'"，以此类推
 */
export function dealNumToVar(text) {
	let num = 0; // 用于编号替换后的占位符
	let numObj = {} // 存储替换后的占位符及其对应的原始数字或容量单位

	// 替换文本中的数字和容量单位为占位符，同时创建一个记录这些占位符及其对应值的对象
	text = text.replaceAll(/[0-9]+(mb|m|MB)?|\{.*?\}/g, str => {
		// 如果文本中包含花括号，则不进行替换，避免影响原有的{data1}等表达式
		if (text.includes('{')) return str

		// 根据匹配到的内容判断是数字还是容量单位，分别设置key为num或size
		let key = /mb|m|MB/.test(str) ? 'size' : 'num'
		num++ // 占位符编号自增
		numObj[`${key}${num}`] = `'${str}'` // 记录占位符及其原始值
		return `{${key}${num}}` // 替换为占位符
	})

	return [text, numObj] // 返回处理后的文本和占位符对象
}

// 目前简单的处理为一个大的class，后续看有没有需要更加细分
export default class Transfer {
	constructor(templateText, basePath, textNum, { fileType = 'vue', isAllPath = true, langRefer = false }) {
		// 记录下替换了多少个
		this.replaceTextCount = 0
		// 用于存储各种key的起始值，比如text为10的话，那下一个key就是text11
		this.countObj = {
			text: textNum
		}
		// 以中文key存储路径
		this.commonCnReverse = void 0;
		this.langRefer = langRefer
		// 新增的文案存储，中文key
		this.extractObjReverse = langRefer ? historyLangObj : {}
		// 新增的文案存储，路径作为key
		this.extractObj = {}
		// 待提取的代码
		this.templateText = templateText
		// 基础路径，比如'aaa.bbb'，那中文替换后路径就是aaa.bbb.text1
		this.basePath = basePath
		// 文件类型，用于更准确的处理代码
		this.fileType = fileType
		// 是否展示所有路劲，还是直接返回第一个路径（路径的合理性问题，建议是全路径）
		// TODO 考虑出个智能模式，智能判断，比如有common的考虑直接返回common，没有common的返回全路径
		this.isAllPath = isAllPath
	}
	// 根据输入的首个路径初始化排序，目前排序只排一层
	initCommonCnReverse(path) {
		let moduleSort = ['common']
		let currModule = path ? path.split('.')[0] : ''
		currModule && moduleSort.unshift(currModule)

		if (!this.commonCnReverse || JSON.stringify(moduleSort) !== JSON.stringify(moduleSortPrev)) {
			this.commonCnReverse = getCommonCnReverse(this.isAllPath)
		}
		delete this.commonCnReverse['']
	}
	// 做个统一处理，文案匹配去重，以及变量处理，返回匹配或者生成的路径
	addToObj(text, dataObj, name = 'text') {
		this.replaceTextCount++ // 统计总共替换了多少个
		// 处理文案，提取数字
		let numDealRes = dealNumToVar(text)
		if (numDealRes[1] && dataObj) {
			text = numDealRes[0]
			Object.assign(dataObj, numDealRes[1])
		}
		// 匹配'确 定' '取 消'这种情况，去掉空格
		if (/[\u4e00-\u9fa5]\s[\u4e00-\u9fa5]/.test(text)) {
			text = text.replace(/\s/, '')
		}
		let existPath = this.commonCnReverse[text] || this.extractObjReverse[text];
		if (existPath) {
			return existPath
		}
		// 对于有动态值的格式的匹配
		if (dataObj?.data1 || dataObj?.num1) {
			let shortReg = /(?<=\{).*?(?=\})/g
			let replaceArr = []
			// 将{}的内容处理成xxx，用于后续核对
			let textReplace = text.replaceAll(shortReg, str => {
				replaceArr.push({
					label: str,
					value: dataObj[str]
				})
				return 'xxx'
			})
			const allNameObj = Object.assign({}, this.commonCnReverse, this.extractObjReverse)
			for (let key of Object.keys(allNameObj)) {
				// 简单匹配下{前的和}后的，看看有没有可能一致
				if (key.startsWith(text.split('{')[0]) && key.endsWith(text.split('}').at(-1))) {
					let dataArr = []
					// 将${}中的内容替换为xxx
					let replacedKey = key.replaceAll(shortReg, dataName => {
						// let content = `data${dataArr.length + 1}`
						// dataArr.push({content, dataName})
						dataArr.push(dataName)
						return `xxx`
					})
					// 进行一次精准匹配，如果匹配上，就把路径和值都替换掉
					if (replacedKey === textReplace) {
						// dataArr.forEach(({content, dataName}) => {
						// 	dataObj[dataName] = dataObj[content]
						// 	delete dataObj[content]
						// })
						clearObj(dataObj)
						replaceArr.forEach(({ label, value }, index) => {
							dataObj[dataArr[index]] = value
							// delete dataObj[label]
						})
						return allNameObj[key]
					}
				}
			}
			// 如果没匹配上，就是新增的，把新增的dataxx根据被替换的原内容调整，尝试变的有意义
			text = dealDataToMeaningful(dataObj, text)
		}

		if (!this.countObj[name]) this.countObj[name] = 0
		let beSetKey, existValue = true
		// 循环判断是否存在重复值，存在的话就增加数字
		while (existValue) {
			this.countObj[name]++
			beSetKey = name + this.countObj[name]
			existPath = `${path}.${beSetKey}`
			existValue = getObjValueFromPath(existPath)
		}
		// 临时存储，用于新增内容的去重
		this.extractObj[beSetKey] = text
		this.extractObjReverse[text] = existPath
		return existPath
	}
	// >换行tab中文tab换行<，这种情况（文案中存在<>的时候会有问题，待考虑~）
	domStringTrans(str, tStr = '$t', { tag, staticClass }) {
		const keyName = dealSpectialName(staticClass) || dealSpectialName(tag, 'tag')
		// 截取前后的换行符及空格等（不截取会被当做正式文案~）
		let startTrimStr = '', endTrimStr = ''
		str = str.replace(/^[\n\t\s]*/, str => {
			startTrimStr = str
			return ''
		})
		str = str.replace(/[\n\t\s]*$/, str => {
			endTrimStr = str
			return ''
		})

		// 对于{{}}内的中文先做处理
		let shortReg = /\{\{.*?\}\}/gs
		let resText = str.replaceAll(shortReg, str => {
			const dealRes = this.defaultDeal(str.slice(2, -2), undefined, keyName)
			return '{{' + `${dealRes}` + '}}'
		})
		// 转化 中文：{{}} 这种情况
		let spectialReg = /^[^\{\}]*[\u4e00-\u9fa5]+[^\{\}]*(?=：\{\{(?!(\\1|\\4)).*?\}\}$)/gs
		if (spectialReg.test(resText)) {
			resText = resText.replaceAll(spectialReg, str => {
				// TODO: 加上提取数字的逻辑，暂时没有新的测试例子，所以没加
				const pathChange = this.addToObj(str, undefined, keyName)
				return `\{\{$t('${pathChange}')\}\}`
			})
		}
		// 转化 中文（{{}}）这种情况
		let spectialReg2 = /^[^\{\}]*[\u4e00-\u9fa5]+[^\{\}]*(?=（\{\{(?!(\\1|\\4)).*?\}\}）$)/gs
		if (spectialReg2.test(resText)) {
			resText = resText.replaceAll(spectialReg2, str => {
				// TODO: 加上提取数字的逻辑，暂时没有新的测试例子，所以没加
				const pathChange = this.addToObj(str, undefined, keyName)
				return `\{\{$t('${pathChange}')\}\}`
			})
		}
		// 检查剩余部分是否有中文
		if (/[\u4e00-\u9fa5]+/.test(resText)) {
			let dataObj = {} // 存储变量的原内容
			let dataNum = 0
			// 把{{}}的内容提取成变量
			let replacedText = resText.replaceAll(shortReg, data => {
				let dataValue = data.slice(2, -2)
				dataNum++
				let key = `data${dataNum}`
				dataObj[key] = dataValue
				return `\{${key}\}`
			})

			const firstStr = replacedText[0], finalStr = replacedText.at(-1)
			let startStr = '', endStr = ''

			if (/[:：]/.test(finalStr)) {// 判断冒号结尾
				replacedText = replacedText.slice(0, -1)
				endStr = finalStr
			} else if (/^[(（][\u4e00-\u9fa5]+[）)]$/.test(str)) {// 判断括号等字符
				replacedText = replacedText.slice(1, -1)
				startStr = firstStr
				endStr = finalStr
			}

			const pathChange = this.addToObj(replacedText, dataObj, keyName)
			let dataObjStr = Object.keys(dataObj).length ? `, {${objToStr(dataObj, false)}}` : ''
			resText = startStr + '{{' + `${tStr}('${pathChange}'` + dataObjStr + ')}}' + endStr
		}
		return startTrimStr + resText + endTrimStr
	}
	// 模板字符串内部的替换（对于用于dom结构的文案无法区分，需要注意）
	templateStringTrans(text, tStr = '$t', name) {
		return text.replaceAll(/`.*?`|(\/\*\*)((?!\\1).*?)(\*\/)|\/\/[^\n]*\n/gs, str => {
			// 注释的判断还是存在问题，可能内部的//也会被过滤掉
			if (/(\/\*\*)((?!\\1).*?)(\*\/)/gs.test(str) || /\/\/[^\n]*\n/.test(str)) return str
			if (!/[\u4e00-\u9fa5]+/.test(str)) return str
			str = str.slice(1, -1)
			// 对于${}内的中文先做处理
			let shortReg = /\$\{.*?\}/gs
			let resText = str.replaceAll(shortReg, str2 => {
				const dealRes = this.defaultDeal(str2.slice(2, -1))
				return '${' + `${dealRes}` + '}'
			})
			// 转化 中文：{{}} 这种情况
			let spectialReg = /^[^$\{\}]*[\u4e00-\u9fa5]+[^$\{\}]*(?=[：:](\$\{)((?!(\\1|\\4)).*?)(\})$)/gs
			if (spectialReg.test(str)) {
				resText = str.replaceAll(spectialReg, str => {
					// TODO: 加上提取数字的逻辑，暂时没有新的测试例子，所以没加
					const pathChange = this.addToObj(str.slice(1))
					return '${' + tStr + '(' + pathChange + ')}'
				})
			}
			// let endStr = ''
			// 匹配中文${}（想了下还是先不做处理，翻译语言需要保证语句的完整性）
			// if(/([\u4e00-\u9fa5]+)(\$\{.*?\})$/gs.test(resText)) {
			// 	let data = /([\u4e00-\u9fa5]+)(\$\{.*?\})/gs.exec(resText)
			// 	resText = data[1]
			// 	endStr = data[2]
			// }
			// 检查剩余部分是否有中文
			if (/[\u4e00-\u9fa5]+/.test(resText)) {
				// groupArr = resText.match(shortReg)
				let dataObj = {}
				let dataNum = 0
				const replacedText = resText.replaceAll(shortReg, data => {
					let dataValue = data.slice(2, -1)
					dataNum++
					let key = `data${dataNum}`
					dataObj[key] = dataValue
					return `\{${key}\}`
				})
				const pathChange = this.addToObj(replacedText, dataObj, name)
				let dataObjStr = Object.keys(dataObj).length ? `, {${objToStr(dataObj, false)}}` : ''
				resText = `${tStr}('${pathChange}'` + dataObjStr + ')'
			}
			// if(endStr) {
			// 	resText = '${' + resText + '}'
			// 	resText = '`' + resText + endStr + '`'
			// }
			return '`' + resText + '`'
		})
	}
	// 通用处理
	defaultDeal(text, tStr = '$t', name) {
		const squaReg = /("|')((?!\1).)*?\1|console\.(log|error|warn|info)\([^\(\)]*?\)|\/\/[^\n]*\n|(\/\*\*)((?!\\1).*?)(\*\/)|if\s*\(((?!if).+?)\)|\/[^/]+\//gs
		text = text.replaceAll(squaReg, str => {
			// 没有中文的不做处理
			if (!/[\u4e00-\u9fa5]/.test(str)) return str
			// 排除掉正则中的引号干扰
			if (/\/[^/]+\//.test(str)) return str
			// 过滤掉xx == ''和case ''这种和中文比较的情况，因为一般是值，不适合提取中文
			if (/(\!=\s+|\={2, 3}\s+|case\s*)("|')/.test(str)) return str
			// 排除掉console、注释的部分、if的内容，不做处理
			if (str.includes('console') || /\/\/[^\n]*\n/.test(str) || /(\/\*\*)((?!\\1).*?)(\*\/)/gs.test(str) || /if\s*\(((?!if).+?)\)/gs.test(str)) return str

			str = str.slice(1, -1)

			const firstStr = str[0], finalStr = str.at(-1)
			let startStr = '', endStr = ''

			if (/[:：(（)]/.test(finalStr)) {// 判断冒号结尾
				str = str.slice(0, -1)
				endStr = finalStr
			} else if (/^[(（][\u4e00-\u9fa5]+[）)]$/.test(str)) {// 判断括号等字符
				str = str.slice(1, -1)
				startStr = firstStr
				endStr = finalStr
			}
			if (startStr) startStr = `'${startStr}' + `
			if (endStr) endStr = ` + '${endStr}'`

			let dataObj = {}
			const pathChange = this.addToObj(str, dataObj, name)
			let dataObjStr = Object.keys(dataObj).length ? `, {${objToStr(dataObj, false)}}` : ''
			return `${startStr}${tStr}('${pathChange}'${dataObjStr})${endStr}`
		})
		text = this.templateStringTrans(text, tStr, name)
		return text
	}
	commonFunc(tStr) {
		return str => {
			const defaultDeal = this.defaultDeal.bind(this)
			str = objDeal(str, tStr, defaultDeal)
			str = this.defaultDeal(str, tStr)
			return str
		}
	}
	dealNodes(nodes, arr = [], parentNode) {
		nodes.forEach(element => {
			if (!element.parent && parentNode) {
				element.parent = parentNode
			}
			const { attrsList, children, text, type, tag, parent, ifConditions, scopedSlots } = element
			// type为2的时候，tokens: ['xxx', {@bingding: "xxx ? xxx : xxx"}]
			if ((type === 3 || type === 2) && isIncludeZh(text)) { // 纯文本节点或者{{xxx}}中的
				const dealRes = this.domStringTrans(text, void 0, parent)
				arr.push([
					text,
					dealRes
				])
			}
			if (attrsList?.length) {
				attrsList.forEach(attr => {
					const { name, value } = attr
					// 动态绑定的属性值处理
					if (/^([:@][\w-]+|v-html)/.test(name) && isIncludeZh(value)) {
						const keyName = dealSpectialName(name)
						arr.push([
							value,
							`${this.defaultDeal(value, void 0, keyName)}`
						])
					} else if (isIncludeZh(value)) { // 静态的
						let str = value
						const firstStr = str[0], finalStr = str.at(-1)
						let startStr = '', endStr = ''

						// 判断冒号结尾
						if (/[:：]/.test(finalStr)) {
							str = str.slice(0, -1)
							endStr = finalStr
						}

						if (/^[(（][\u4e00-\u9fa5]+[）)]$/.test(str)) {
							str = str.slice(1, -1)
							startStr = firstStr
							endStr = finalStr
						}
						let dataObj = {}
						const keyName = dealSpectialName(name)
						const pathChange = this.addToObj(str, dataObj, keyName)
						// 有变量的话就加上
						let dataObjStr = Object.keys(dataObj).length ? `, {${objToStr(dataObj, false)}}` : ''
						// 存在被排除的字符时，需要转为模板字符串
						if (startStr || endStr) {
							arr.push([
								// 这里需要带上name，因为要加上:
								[`${name}="${value}"`, `${name}='${value}'`],
								`:${name}="\`\$\{${startStr}$t('${pathChange}'${dataObjStr})\}${endStr}\`"`
							])
						} else {
							arr.push([
								// 这里需要带上name，因为要加上:
								[`${name}="${value}"`, `${name}='${value}'`],
								`:${name}="$t('${pathChange}'${dataObjStr})"`
							])
						}
					}
				})
			}
			if (ifConditions?.length > 1) this.dealNodes(ifConditions.slice(1).map(v => v.block), arr, parentNode)
			children && this.dealNodes(children, arr, element)
			scopedSlots && this.dealNodes(Object.values(scopedSlots), arr, element)
		});
		return arr
	}
	main() {
		this.initCommonCnReverse(this.basePath);
		// template标签内单独处理，这部分中文没有引号，需要优先处理，否则会对引号判断造成干扰
		// template里面有//可能会出现问题，不过有//的可能性较小，暂时无视~
		// 对于文案内部嵌入dom的内容，暂时没想到怎么区分，也先手动处理
		let templateText = this.templateText;
		if (this.fileType === 'vue-tem') {
			templateText = `<template>${text}</template>`
		}
		let textRes = templateText.replace(/<template>.*<\/template>/gs, temStr => {
			// 做个替换，不然解析有问题~
			temStr = temStr.slice(10, -11)
			temStr = `<tplt1111>${temStr}</tplt1111>`
			const nodeData = getTemplateNode(temStr)
			if (nodeData.errors?.length) {
				console.log(nodeData.errors, 'errors')
				nalert('dom解析遇到了意外的错误，dom部分暂时不做处理，如有需要自行根据控制台的错误输出调整下dom结构中的问题~')
				return temStr
			}
			const arr = this.dealNodes([nodeData.ast])
			// 做替换，这种方式可能有点风险，后续遇到问题再继续处理~
			arr.forEach(([originText, newText]) => {
				// 对于属性无法判断原本的引号格式的特殊处理情况的处理，先这样~
				if (originText instanceof Array) {
					for (let text of originText) {
						if (temStr.includes(text)) {
							temStr = temStr.replace(text, newText)
							return
						}
					}
				}
				temStr = temStr.replace(originText, newText)
			});
			temStr = temStr.slice(10, -11)
			temStr = `<template>${temStr}</template>`
			return temStr
		})

		// TODO，思考精确的匹配方案
		// setup中有引号中文的，需要在结尾加上//setupEnd才能匹配
		textRes = textRes.replace(/setup\(.*\/\/setupEnd/gs, this.commonFunc())
		// setup中有引号中文的
		textRes = textRes.replace(/<script\s+setup>.*?<\/script>/gs, this.commonFunc())
		// script内的处理
		textRes = textRes.replace(/<script\s*>.*?<\/script>/gs, this.commonFunc('this.$t'))

		// 选择js或者vuejs之后，做针对性处理，目前两者区别主要是加不加this
		if (/js/.test(this.fileType)) {
			textRes = this.commonFunc(this.fileType === 'vuejs' ? 'this.$t' : '$t')(textRes)
		}

		if (!this.langRefer) {
			Object.assign(historyLangObj, this.extractObjReverse)
		}

		return [textRes, objToStr(this.extractObj)]
	}
}

window.textTransfer = {
	Transfer,
	copy,
	getCommonCnReverse,
	getObjValueFromPath
}